package com.dnb.webmash.playmaker.client.ui;

import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;

import com.dnb.webmash.playmaker.client.AppConstants;
import com.dnb.webmash.playmaker.client.UniqueTreeItem;
import com.dnb.webmash.playmaker.client.WebPlaylistService;
import com.dnb.webmash.playmaker.client.WebPlaylistServiceAsync;
import com.dnb.webmash.playmaker.client.mediaplayer.MediaPlayersManager;
import com.dnb.webmash.playmaker.shared.MediaType;
import com.dnb.webmash.playmaker.shared.MyURL;
import com.dnb.webmash.playmaker.shared.PlayerType;
import com.dnb.webmash.playmaker.shared.PlaylistItem;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Tree;
import com.google.gwt.user.client.ui.TreeItem;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.CheckBox;


public class MediaTypesTab extends Composite {
	private static final AppConstants CONSTANTS = GWT.create(AppConstants.class);	
	private final static WebPlaylistServiceAsync webPlaylistService = GWT.create(WebPlaylistService.class);
	private final LinkedHashMap<TreeItem, PlaylistItem> playlistItems = new LinkedHashMap<TreeItem, PlaylistItem>();
	private final LinkedHashMap<TreeItem, TreeItem> nowPlayingItems = new LinkedHashMap<TreeItem, TreeItem>();
	private final LinkedHashMap<UniqueTreeItem, PlaylistItem> playableItems = new LinkedHashMap<UniqueTreeItem, PlaylistItem>();
	private final LinkedHashMap<MediaType, Boolean> typesFilter = new LinkedHashMap<MediaType, Boolean>(); //TODO IMPLEMENT FILTERING THE TREE WITH THIS
	private final LinkedHashMap<MyURL, TreeItem> requestedURLS = new LinkedHashMap<MyURL, TreeItem>();
	private final Tree tree_1 = new Tree();
	private final Label errorLabel_1 = new Label();
	private VerticalPanel mediatypesVerticalPanel = new VerticalPanel();
	private final MediaPlayersManager mpMan;

	public MediaTypesTab(){
		/*START OF MEDIATYPES TAB
		 *
		 ****************************************************************************************************************/										
		final DockLayoutPanel tab1_mediaTypesDockLayoutPanel = new DockLayoutPanel(Unit.EM);

		HorizontalPanel horizontalPanel_1 = new HorizontalPanel();
		horizontalPanel_1.setSpacing(3);
		tab1_mediaTypesDockLayoutPanel.addNorth(horizontalPanel_1, 2.5);

		Button resetButton = new Button(CONSTANTS.resetButton_html());
		resetButton.setTitle(CONSTANTS.resetButton_title());
		resetButton.addKeyUpHandler(new KeyUpHandler() {
			@Override
			public void onKeyUp(KeyUpEvent event) {
				if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
					mediatypesVerticalPanel.clear();
					MediaTypesTab.this.requestMediatypesListFromServer();
				}					
			}
		});
		resetButton.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				mediatypesVerticalPanel.clear();
				MediaTypesTab.this.requestMediatypesListFromServer();
			}
		});
		resetButton.setText(CONSTANTS.resetButton_text());
		horizontalPanel_1.add(resetButton);

		Button btnUpdate = new Button(CONSTANTS.btnUpdate_html());
		btnUpdate.setTitle(CONSTANTS.btnUpdate_title());
		btnUpdate.addKeyUpHandler(new KeyUpHandler() {

			@Override
			public void onKeyUp(KeyUpEvent event) {
				MediaTypesTab.this.requestLoadMediaDB();
			}

		});
		btnUpdate.addClickHandler(new ClickHandler(){

			@Override
			public void onClick(ClickEvent event) {
				MediaTypesTab.this.requestLoadMediaDB();
			}

		});

		horizontalPanel_1.add(btnUpdate);
		
		Button btnAddType = new Button(CONSTANTS.btnAddType_html());
		btnAddType.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				final MyPopup popup = new MyPopup(MediaTypesTab.this);
				popup.setPopupPositionAndShow(new PopupPanel.PositionCallback() {
			          public void setPosition(int offsetWidth, int offsetHeight) {
			            int left = (Window.getClientWidth() - offsetWidth) / 3;
			            int top = (Window.getClientHeight() - offsetHeight) / 3;
			            popup.setPopupPosition(left, top);
			          }
			        });
			}
		});
		horizontalPanel_1.add(btnAddType);
		horizontalPanel_1.add(errorLabel_1);
		ScrollPanel scrollPanel = new ScrollPanel();
		tab1_mediaTypesDockLayoutPanel.addWest(scrollPanel, 9.5);
		scrollPanel.setSize("100%", "100%");
		scrollPanel.setWidget(mediatypesVerticalPanel);
		mediatypesVerticalPanel.setSize("100%", "100%");
		
		ScrollPanel scrollPanel_1 = new ScrollPanel();
		HorizontalPanel centerHP = new HorizontalPanel();
		centerHP.setSize("100%", "100%");
		centerHP.add(tree_1);

		this.tree_1.addSelectionHandler(new SelectionHandler<TreeItem>() {
			public void onSelection(SelectionEvent<TreeItem> event) {
				final TreeItem sel = event.getSelectedItem();								
				PlaylistItem plsit = getPlaylistItemFromTreeItem(sel);				
				if (plsit!=null){								
					if (plsit.isPlayable())
						MediaTypesTab.this.embedPlsItem(plsit);
				}
				else
					MediaTypesTab.this.errorLabel_1.setText("No PlaylistItem linked to selected TreeItem!");//THis means the playable item selected wasnt added to the tree!
			}
		});
		tree_1.setSize("100%", "100%");
		mpMan = new MediaPlayersManager();
		centerHP.add(mpMan);
		mpMan.setSize("100%", "100%");
		scrollPanel_1.setWidget(centerHP);
		tab1_mediaTypesDockLayoutPanel.add(scrollPanel_1); //TODO
		scrollPanel_1.setSize("100%", "100%");
		
		this.initWidget(tab1_mediaTypesDockLayoutPanel);
	}

	public void onLoad()
	{
		MediaTypesTab.this.requestMediatypesListFromServer();
	}

	@Override
	public void setWidth(String width) {//TODO add to prefs
		super.setWidth(width);
	}
	@Override
	public void setHeight(String height) {
		super.setHeight(height);
	}

	/* END OF MEDIATYPES TAB
	 * START OF HELPERS
	 */

	/*
	 * Adds playlist to tree view and to the master playableItems list
	 * @returns the last TreeItem added to the tree
	 */
	private Boolean addPlaylist(ArrayList<PlaylistItem> vals, Tree tree, Label myLabel) {				
		TreeItem parent = null;
		Boolean addedSomething = false;
		for (int i = 0; i < vals.size(); i++) {//each one should be a playlist: a URL root with media and links within
			final PlaylistItem it = vals.get(i);
			if (requestedURLS.containsKey(it.getURL())){tree.removeItem(requestedURLS.get(it.getURL()));//remove the nodes about to be updated
			}
			parent = new TreeItem();
			parent.setText(it.getName());
			tree.addItem(parent);//add it
			this.playlistItems.put(parent,it);
			if (it.getURL()!=null) {this.requestedURLS.put(it.getURL(),parent);	//Remember the added playlists								
			}
			if (it.hasChildren()) { //did we find anything?
				addedSomething = true;
				this.addPlaylistNode(it.getChildren(), parent);
			} else {//empty								
				tree.removeItem(parent);
				if (myLabel!=null) myLabel.setText("No media in playlist: "+ it.getName());
			}
		}
		return addedSomething; //returns the last element added
	}

	// recursive helper method
	private void addPlaylistNode(LinkedHashSet<PlaylistItem> vals, TreeItem branch) {		//TODO HERE ADD CODE TO STORE A mini NOW PLAYING IN EACH REQUESTED NODE, WITH THEIR MEDIA ONLY
		Iterator<PlaylistItem> iter = vals.iterator();
		while (iter.hasNext()){
			PlaylistItem it = iter.next();
			if (it.hasChildren()) {
				TreeItem parent = new TreeItem();
				parent.setText(it.getName());
				branch.addItem(parent);
				this.playlistItems.put(parent, it);
				this.addPlaylistNode(it.getChildren(), parent);
			} else {
				final UniqueTreeItem ti = new UniqueTreeItem(it.getName());
				branch.addItem(ti);
				this.playlistItems.put(ti, it);
				if (it.isPlayable()) this.playableItems.put(ti, it);// has no children so could be playable item
			}
		}
	}

	private PlaylistItem getPlaylistItemFromTreeItem(TreeItem sel){
		PlaylistItem item = MediaTypesTab.this.playlistItems.get(sel);
		if (item==null) {//might be in now playing
			final TreeItem nowPlaying = MediaTypesTab.this.nowPlayingItems.get(sel);
			item = MediaTypesTab.this.playableItems.get(nowPlaying);			
			// TODO add highlighting to now playing node using font color or css (leave tree.setSelected)
		}
		return item;
	}
	private boolean embedPlsItem(PlaylistItem sel) {
		if ((sel!=null) && (sel.isPlayable())){								
			return this.mpMan.embedItem(sel);
		} else
			this.errorLabel_1.setText("Error embedding media!");
		return false;
	}
	//STATIC POPUP METHOD for saving new mediatypes
	private static class MyPopup extends PopupPanel {

	    public MyPopup(final MediaTypesTab mTT) {
	      super(false);
	      //add stuff to the popup
	      	HorizontalPanel horizontalPanel = new HorizontalPanel();
	      	horizontalPanel.setSize("100%", "100%");
	      	VerticalPanel inputFieldsVerticalPanel = new VerticalPanel();
			inputFieldsVerticalPanel.setSize("100%", "100%");
			
			Label nameLabel = new Label("Name");
			inputFieldsVerticalPanel.add(nameLabel);
			final TextBox nameTextBox = new TextBox();
			inputFieldsVerticalPanel.add(nameTextBox);
			
			Label testPatLabel = new Label("Test Pattern");
			inputFieldsVerticalPanel.add(testPatLabel);
			final TextBox testPatternTextBox = new TextBox();
			inputFieldsVerticalPanel.add(testPatternTextBox);
			
			Label mediaIDPatsLabel = new Label("Media ID Patterns");
			inputFieldsVerticalPanel.add(mediaIDPatsLabel);
			final TextArea mediaIDPattsTextArea = new TextArea();
			inputFieldsVerticalPanel.add(mediaIDPattsTextArea);
			
			Label sourceURLLabel = new Label("Source URL");
			inputFieldsVerticalPanel.add(sourceURLLabel);
			final TextBox sourceURLTextBox = new TextBox();
			inputFieldsVerticalPanel.add(sourceURLTextBox);
			
			Label playerTypeLabel = new Label("Player Type");
			inputFieldsVerticalPanel.add(playerTypeLabel);
			final ListBox comboBox_1 = new ListBox();
			inputFieldsVerticalPanel.add(comboBox_1);
			
			for (PlayerType t : PlayerType.values()){
				comboBox_1.addItem(t.name());
			}
			final HTML descriptionsLabel = new HTML();//loads the player descriptions
			descriptionsLabel.setHTML(PlayerType.getDescription(comboBox_1.getSelectedIndex()));
			comboBox_1.addChangeHandler(new ChangeHandler() {
				@Override
				public void onChange(ChangeEvent event) {
					descriptionsLabel.setHTML(PlayerType.getDescription(comboBox_1.getSelectedIndex()));
				}
			});
			
			Button okButton = new Button("OK");
			okButton.addClickHandler(new ClickHandler() {
				
				@Override
				public void onClick(ClickEvent event) {
					//TODO VERIFY IF ALL REQUIRED FIELDS ARE FILLED
					MediaType myNewType = new MediaType(testPatternTextBox.getText(), new ArrayList<String>(Arrays.asList(mediaIDPattsTextArea.getText().split("\n"))),sourceURLTextBox.getText(), nameTextBox.getText(),PlayerType.get(comboBox_1.getSelectedIndex()));
					mTT.requestCreateNewMediaType(myNewType);
					MyPopup.this.hide();
				}
			});
			inputFieldsVerticalPanel.add(okButton);
			Button cancelButton = new Button("Cancel");
			cancelButton.addClickHandler(new ClickHandler() {
				
				@Override
				public void onClick(ClickEvent event) {
					MyPopup.this.hide();
				}
			});
			inputFieldsVerticalPanel.add(cancelButton);
			horizontalPanel.add(inputFieldsVerticalPanel);
			horizontalPanel.add(descriptionsLabel);
			setWidget(horizontalPanel);
	    }
	  }
		//ASYNC REQUEST METHODS
	private void requestLoadMediaDB(){
		ArrayList<String> request = new ArrayList<String>();
		for (MediaType mt : typesFilter.keySet()){
			if (typesFilter.get(mt)) request.add(mt.getName());
		}
		MediaTypesTab.webPlaylistService.requestLoadPls(request, new AsyncCallback<ArrayList<PlaylistItem>>(){

			@Override
			public void onFailure(Throwable caught) {
				MediaTypesTab.this.errorLabel_1.setText("There was an error on the server... please try again later.");
			}

			@Override
			public void onSuccess(ArrayList<PlaylistItem> result) {
				if (result!=null) {
					MediaTypesTab.this.errorLabel_1.setText("");
					if (!result.isEmpty()){
						for (PlaylistItem p : result) {
							if (!p.hasChildren())
								result.remove(p);
						}
						//final TreeItem playlist = MediaTypesTab.this.addPlaylist(result, MediaTypesTab.this.tree_1, MediaTypesTab.this.errorLabel_1);				
						if (!MediaTypesTab.this.addPlaylist(result, MediaTypesTab.this.tree_1, null))
							MediaTypesTab.this.errorLabel_1.setText("Could not find any media to add for the selected types!");
						else MediaTypesTab.this.errorLabel_1.setText("");
					} else MediaTypesTab.this.errorLabel_1.setText("No media has been stored for the selected mediatypes");
				} else
					MediaTypesTab.this.errorLabel_1.setText("No Media at URL... or Connection Failed");				
			}
		});
		this.errorLabel_1.setText("Loading Media DB for selected types...");
	}

	private void requestCreateNewMediaType(MediaType nT){
		MediaTypesTab.webPlaylistService.requestCreateNewMedia(nT, new AsyncCallback<Boolean>(){

			@Override
			public void onFailure(Throwable caught) {
					MediaTypesTab.this.errorLabel_1.setText("Failed to create new media");
				}

			@Override
			public void onSuccess(Boolean result) {
				MediaTypesTab.this.errorLabel_1.setText("New media created!");
			}
			
		});
	}


	private void requestMediatypesListFromServer()	throws IllegalArgumentException {
		MediaTypesTab.webPlaylistService.requestAllPlayTypes(new AsyncCallback<ArrayList<MediaType>>(){

			public void onFailure(Throwable caught) {
				// Show the RPC error message to the user
				MediaTypesTab.this.errorLabel_1.setText("There was an error on the server... please try again later.");
			}

			@Override
			public void onSuccess(ArrayList<MediaType> result) {		
				CheckBox checkBox;
				for (MediaType mt : result){ //first run through update the UI
					typesFilter.put(mt, true);//all types enabled by default
					checkBox = new CheckBox(mt.getName());
					checkBox.setValue(true);
					mediatypesVerticalPanel.add(checkBox);
				}
				MediaTypesTab.this.requestLoadMediaDB();
			}			
		});
		this.errorLabel_1.setText("Mediatypes Query in progress...");
	}

}
